package com.ilearn.actor;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;

public final class ActorContext {
	
	private final String path;
	private final BlockingQueue<Object[]> box = new LinkedBlockingQueue<Object[]>();
	private final ActorSystem system;
	private final Actor actor;
	private volatile boolean suspend = false;
	
	private final AtomicBoolean lock = new AtomicBoolean(false);
	private volatile long lockTime = 0;
	private volatile long dispatchTime = 0;
	
	public ActorContext(ActorSystem system, String path, Actor actor) {
		this.system = system;
		this.path = path;
		this.actor = actor;
	}
	
	public boolean lock() {
		boolean ret = lock.compareAndSet(false, true);
		if(ret)
			this.lockTime = System.currentTimeMillis();
		return ret;
	}
	
	public int lockTime() {
		return (int) (this.lockTime <=0 ? 0: System.currentTimeMillis() - this.lockTime);
	}
	
	public void unlock() {
		this.lockTime = 0;
		lock.set(false);
	}
	
	public Actor actor() {
		return this.actor;
	}
	
	public ActorSystem system() {
		return this.system;
	}
	
	protected Object[] poll() {
		return this.box.poll();
	}
	
	public boolean empty() {
		return box.isEmpty();
	}
	
	public int count() {
		return box.size();
	}
	
	public String path() {
		return this.path;
	}
	
	public boolean isSuspend() {
		return this.suspend;
	}
	
	public void suspend(boolean flag) {
		this.suspend = flag;
		if(!flag && !empty())
			this.system.monitor(this);
	}

	public void tell(Object msg) {

		if(system.status() != ActorSystem.RUN)
			throw new RuntimeException("Actor System closed.");
		tell(msg, null);
	}

	/**
	 * 发送消息，如果ActorSystem状态不为RUN，则抛出运行时异常
	 * @param msg
	 * @param cb
	 */
	public void tell(Object msg, CallBack cb) {
		if(system.status() != ActorSystem.RUN)
			throw new RuntimeException("Actor System closed.");
		tell2(msg, cb);
	}

	/**
	 * 发送消息，无回调
	 * @param msg
	 */
	public void tell2(Object msg) {
		
		tell2(msg, null);
	}

	/**
	 * 发送消息，回调函数，其实是将消息先存放于mail box中，其实就是阻塞队列
	 * @param msg
	 * @param cb
	 */
	public void tell2(Object msg, CallBack cb) {
		
		try {
			this.box.put(cb != null ? new Object[]{msg, cb}: new Object[]{msg});
			system.monitor(this);
		} catch (InterruptedException e) {
			
			e.printStackTrace();
		}
	}

	public long getDispatchTime() {
		return dispatchTime;
	}

	public void setDispatchTime(long dispatchTime) {
		this.dispatchTime = dispatchTime;
	}
	
	@Override
	public String toString() {
		return "ActorContext-"+this.actor.getClass().getSimpleName();
	}
	
}


